package com.sql.pool;


import java.io.IOException;
import java.sql.Connection;

import java.sql.SQLException;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.sql.handler.Deploy;
import com.sql.handler.VectorConnection;

import com.sql.handler.util.LogFactory;


public class FixedConnectionPool extends AbstractConnectionPool{  
    
	/*线程安全数组*/
    private static volatile VectorConnection<Connection> pool = new VectorConnection<>();
    private static volatile VectorConnection<Connection> recordPool = new VectorConnection<>();
    private static volatile VectorConnection<Connection> recordNewPool = new VectorConnection<>();
    private static volatile Connection selectConnection;
    /*连接池中储存的最大数量*/
    private static int POOL_SIZE;  
    /*当线程池耗尽时，最大新建数量*/
    private static int NEW_POOL_SIZE;
    /*信号量*/
    private static Semaphore semp = null;
    /*检查机制的循环间隔时间*/
    private final static int MS_TIME = 60; 
    /*不同于synchronized的另一种锁机制*/
    private final static Lock SELECT_LOCK = new ReentrantLock();
    private final static Lock RELEASE_LOCK = new ReentrantLock();
    private final static Lock CONNECTION_LOCK = new ReentrantLock();
    /*定时器*/
	private static ScheduledExecutorService scheduledExecutorService;
    /*构造方法，不做任何工作*/  
    private FixedConnectionPool() {}  
    
    /**
     * 做初始化工作
     * @throws InterruptedException 
     */
    private synchronized static void init() throws ClassNotFoundException, SQLException{
    	Logs.d("正在初始化连接池..");
    	semp = new Semaphore(NEW_POOL_SIZE);
    	selectConnection = load();
		for (int i = 0; i < POOL_SIZE; i++) {  
		    try {
				padCachePool();
			} catch (InterruptedException e) {}	
		}  
        Logs.d("初始化完毕");
    	
    }
    
    /**
     * 得到select使用的连接，查询不同于其他三项，查询可以并发进行执行，
     * 所以我们只需提供一个连接来供所有用户使用
     */
    private static Connection getSelectConnection() throws SQLException, InterruptedException, ClassNotFoundException{
    	return isOkConnection(selectConnection) ? selectConnection : loadSelectConnection();
    }
    
    /**
     * 加载查询使用的连接
     * @return
     * @throws InterruptedException
     * @throws SQLException
     * @throws ClassNotFoundException 
     */
    private static Connection loadSelectConnection() throws InterruptedException, SQLException, ClassNotFoundException{
    	if(SELECT_LOCK.tryLock(8L,TimeUnit.SECONDS)){
    		loadSelectConnctionHandler();
    		SELECT_LOCK.unlock();
    	}
    	Logs.d("成功拿到连接");
    	return selectConnection;
    }
  
    /**
     * 加载查询连接的处理方法
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    private static void loadSelectConnctionHandler() throws ClassNotFoundException, SQLException{
    	if(selectConnection == null || selectConnection.isClosed()){
			selectConnection = load();
    	}
    }
    
    
    /**
     * 返回连接到连接池
     * 在这里进行控制，如果连接池里的连接数大于我们规定的数量，则对此连接进行关闭
     */
    private static void release(Connection con) throws SQLException, InterruptedException {
    	if(!isOkConnection(con)){
    		return;
    	}
    	if(pool.contains(con)){
    		return;
    	}
    	releaseHandler(con);
    }  
    
    /**
     * 处理回收连接的方法
     * @param con
     * @throws InterruptedException
     * @throws SQLException
     */
    private static void releaseHandler(Connection con) throws InterruptedException, SQLException{
    	if(pool.size() >= POOL_SIZE){
    		con.close();
    		return;
    	}
    	if(RELEASE_LOCK.tryLock(8L,TimeUnit.SECONDS)){
    		if(pool.size() < POOL_SIZE){
        		Logs.d("成功回收连接");
        		pool.add(con); 
        		RELEASE_LOCK.unlock();
        	} else{
        		con.close();
        		RELEASE_LOCK.unlock();
        	}
    	}
    }
    
    /**
     * 新建连接
     * @return
     * @throws SQLException
     * @throws ClassNotFoundException
     */
    private static Connection getNewConnection() throws SQLException, ClassNotFoundException{
		return newConnectionHandler();
    }
    
    /**
     * 建立新连接的处理方法
     * @return
     * @throws SQLException
     * @throws ClassNotFoundException 
     */
    private static Connection newConnectionHandler() throws SQLException, ClassNotFoundException{
    	if (recordNewPool.size() < NEW_POOL_SIZE){
			Connection conn = load();
			if(isOkConnection(conn)){
				recordNewPool.add(conn);
				Logs.d("成功拿到新建的连接");
				return conn;
			}
		}
    	return null;
    }
  
    /** 返回连接池中的一个数据库连接
     * 如果连接池中已经耗尽了Connextion
     * 则创建新的使用 
     */
    private static Connection getConnection() throws ClassNotFoundException, InterruptedException, SQLException {    
    	Connection conn = getCacheConnection();
		if(isOkConnection(conn)){
			return conn;
		}
		do{
			conn = getConnectionHandler();
		}while(!isOkConnection(conn));
		return conn;
    }  
    
    /**
     * 控制新建连接的数目
     * 通过信号量
     * @return
     * @throws InterruptedException
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    private static Connection controlNewConnection() throws InterruptedException, ClassNotFoundException, SQLException{
    	Connection conn;
    	if (recordNewPool.size() < NEW_POOL_SIZE) {
    		semp.acquire();
    		if(isMaximum(semp)){
    			conn = getNewConnection();
           		semp.release();
        		return conn;
    		}
    		semp.release();
    	}
    	return getWaitConnection();
    }
    
    /**
     * 补充连接池
     * @throws InterruptedException 
     * @throws SQLException 
     * @throws ClassNotFoundException 
     */
    private static synchronized void padCachePool() throws InterruptedException, ClassNotFoundException, SQLException{
    	while((recordPool.size() + pool.size()) < POOL_SIZE){
    		pool.add(load());
    	}
    }
    
    /**
     * 得到连接的处理方法
     * @return
     * @throws InterruptedException
     * @throws SQLException
     * @throws ClassNotFoundException 
     */
    private static Connection getConnectionHandler() throws InterruptedException, SQLException, ClassNotFoundException{
    	Connection conn = controlNewConnection();
    	return isOkConnection(conn) ? conn : null;
    }
    
    private static Connection getCacheHandlerConnection() throws InterruptedException, SQLException{
    	Connection conn;
    	if((recordPool.size() + pool.size()) > POOL_SIZE){
    		return null;
    	}
    	if(CONNECTION_LOCK.tryLock(8L,TimeUnit.SECONDS)){
			while((recordPool.size() + pool.size())< POOL_SIZE){
				conn = load();
				if(isOkConnection(conn)){
					recordPool.add(conn);
					CONNECTION_LOCK.unlock();
					return conn;
				}
			}
			CONNECTION_LOCK.unlock();
		}
    	return null;
    }
    
    /**
     * 对新建连接通过信号量进行控制，这里提供判断条件
     * @return
     */
    private static boolean isMaximum(Semaphore semp){
    	final int i = recordNewPool.size();
		final int j = semp.availablePermits();
		final int z = NEW_POOL_SIZE - j;
		return (z + i) <= NEW_POOL_SIZE ? true : false;
    }
    
    /**
     * 得到缓存中的连接
     * @return
     * @throws SQLException
     */
    private static Connection getCacheConnection() throws SQLException{
    	while(pool.size() > 0){
    		Connection conn = getCacheConnectionHandler();
			if (conn != null) {
				return conn;
			}
		}
    	return null;
    }
    
    /**
     * 得到缓存中的连接的具体实现
     * @return
     * @throws SQLException
     */
    private static Connection getCacheConnectionHandler() throws SQLException{
    	if(pool.size() <= 0) {
    		return null;
    	}
    	Connection conn = pool.remove(0);
		if (isOkConnection(conn)) {
			Logs.d("成功拿到缓存池中的连接");
			return conn;
		}
		return null;
    }
    
    /**
     * 检查每个Connection数组中的连接是否可用
     * 不可用则进行删除
     * @throws SQLException
     */
    private static void checkDisconnect() throws SQLException{
    	Connection conn = null;
    	for (int i = 0;i < recordNewPool.size(); i++) {
    		conn = recordNewPool.get(i);
			if (!isOkConnection(conn)) {
				if(recordNewPool.remove(conn)){
					Logs.d("删除了新建连接记录池中的不可用的连接");
				}
			}
		}
    	for (int i = 0; i < recordPool.size(); i++){
    		conn = recordPool.get(i);
    		if (!isOkConnection(conn)) {
    			if(recordPool.remove(conn)){
    				Logs.d("删除了缓存连接记录池中的不可用的连接");
    			}
			}
    	}
    }
    
    /**
     * 循环等待连接
     * @return
     * @throws InterruptedException
     * @throws SQLException
     * @throws ClassNotFoundException
     */
    private static Connection getWaitConnection() throws InterruptedException, SQLException, ClassNotFoundException{
    	Connection conn = null;
    	Logs.d("正在排队...");
    	while(true){	
			checkDisconnect();
			while(pool.size() > 0){
				conn = pool.remove(0);
				if (isOkConnection(conn)){
					Logs.d("成功拿到缓存池中的连接");
					return conn;
				}

			}
			conn = getCacheHandlerConnection();
			if(isOkConnection(conn)){
				return conn;
			}
			if(recordNewPool.size() < NEW_POOL_SIZE){
				conn = controlNewConnection();
				if(isOkConnection(conn)){
					return conn;
				}
			}
		}
    }
    
    /**
     * 判断连接是否可以使用
     * @param con
     * @return
     * @throws SQLException
     */
    private static boolean isOkConnection(Connection con) throws SQLException{
    	if(con != null && !con.isClosed()){
    		if(!con.isValid(0)){
    			con.close();
    			return false;
    		}
    		return true;
    	}
    	return false;
    }
    
    /**
     * 加载连接
     * @return
     */
    private static Connection load() {	
    	return ConnectionLoad.load(DRIVER_CLASS_NAME, URL, USERNAME, PASSWORD);
    }
    
    /**
     * 获得缓存池中剩余连接的数量
     * @return
     */
    private static int getCacheCount(){
    	return pool.size();
    }
    
    /**
     * 获得新建连接数量的大小
     * @return
     */
    private static int getNewCacheCount(){
    	return recordNewPool.size();
    }
    
    /**
     * 关闭数据库连接池
     * @throws SQLException 
     */
    private static void closeConnectionPool() throws SQLException{
    	selectConnection.close();
    	URL = null;
    	USERNAME = null;
    	PASSWORD = null;
    	DRIVER_CLASS_NAME = null;
    	if(scheduledExecutorService != null && !scheduledExecutorService.isShutdown()){
    		scheduledExecutorService.shutdown();
    	}
    	for(int i = 0;i < pool.size();i++){
    		pool.get(i).close();
    	}
    	for(int i = 0;i < recordNewPool.size();i++){
    		recordNewPool.get(i).close();
    	}
    	for(int i = 0;i < recordPool.size();i++){
    		recordPool.get(i).close();
    	}
    }
    
    /**
     * 内部静态类
     * 为外部访问提供接口
     * 
     * @author xvhuichuang
     * 
     */
    protected static class Builder implements Runnable, IBuilder {
	   
	   private Builder() {}
	   
	   /**
	    * 构造方法
	    * @param deploy
	    * @throws ClassNotFoundException
	    * @throws SQLException
	    * @throws IllegalAccessException
	    * @throws IOException
	    */
	   protected Builder(Deploy deploy)
			   throws ClassNotFoundException, SQLException, IllegalAccessException, IOException{
		   if(deploy == null){
			   throw new IllegalArgumentException("deploy参数不能为null");
		   }
		   if(!isRepeatPool(deploy)){
			   return;
		   }
			initData(deploy);
			openTimerCheck();
	   }
	   
	   /**
	    * 多连接池建立判断
	    */
	   private boolean isRepeatPool(Deploy aDeploy){
		   if(URL != null
				   && USERNAME != null 
				   && PASSWORD != null
				   && DRIVER_CLASS_NAME != null){
			   if(URL == aDeploy.getUrl() 
					   && USERNAME == aDeploy.getUser()
					   && PASSWORD == aDeploy.getPassword()
					   && DRIVER_CLASS_NAME == aDeploy.getDriverClassName()){
				   Logs.d("请不要重复初始化");
				   return false;
			   }
		   }
		   return true;
	   }
	   
	   /**
	    * 当程序被回收，这里进行恢复数据
	    * @param deploy
	    * @throws IllegalAccessException
	    * @throws ClassNotFoundException
	    * @throws SQLException
	    */
	   private void initData(Deploy deploy) throws IllegalAccessException, ClassNotFoundException, SQLException{
		   initDataHandler(deploy);
		   builderInit();
	   }
	   
	   /**
	    * 配置数据
	    * @param cacheSize
	    * @param newMaxSize
	    * @param cycleCheck
	    */
	   private void setConf(int cacheSize,int newMaxSize, boolean isBoolean){
		   if(cacheSize > 0) setCacheSize(cacheSize);
		   if(newMaxSize > 0) setNewMaxSize(newMaxSize);
		   setIsCloseLogs(isBoolean);
	   }
	   
	   /**
	    * 恢复数据的实现方法
	    * @param deploy
	    * @throws IllegalAccessException
	    */
	   private void initDataHandler(Deploy deploy) throws IllegalAccessException{
		   URL = deploy.getUrl();
		   PASSWORD = deploy.getPassword();
		   USERNAME = deploy.getUser();
		   DRIVER_CLASS_NAME = deploy.getDriverClassName();
		   if(URL == null || PASSWORD == null || USERNAME == null || DRIVER_CLASS_NAME == null)
				throw new IllegalAccessException("url、driverClassName、password、user参数不能为空");
		   setConf(deploy.getCacheSize(), deploy.getNewMaxSize(), deploy.getCloseLog());
	   }
	   
	   /**
	    * 是否开启日志
	    */
	   private void setIsCloseLogs(boolean isBoolean) {
		   Logs = LogFactory.create(isBoolean);
	   }

	/**
	    * 设置缓存连接数的大小
	    * @param size
	    */
	   private void setCacheSize(int size){
		   POOL_SIZE = size;
	   }
	   
	   /**
	    * 设置最大能新建的连接数
	    * @param size
	    */
	   private void setNewMaxSize(int size){
		   NEW_POOL_SIZE = size;
	   }
	   
	   /**
	    * 为外部提供初始化数据库连接池的接口
	    * @return
	    * @throws ClassNotFoundException
	    * @throws SQLException
	    */
	   private void builderInit() throws ClassNotFoundException, SQLException{
		   init();
	   }
	   
	   /**
	    * 为外部提供连接的接口
	    * @return
	    */
	   public Connection getBuilderConnection(){
		   try {
			return getConnection();
		} catch (ClassNotFoundException | InterruptedException | SQLException e) {
			e.printStackTrace();
		}
		   return null;
	   }
	   
	   /**
	    * 为外部提供得到查询连接的接口
	    * @return
	    */
	   public Connection getBuilderSelectConnection(){
		   try {
			   return getSelectConnection();
		   } catch (ClassNotFoundException | SQLException | InterruptedException e) {
			   e.printStackTrace();
		   }
		   return null;
	   }
	   
	   /**
	    * 为外部提供回收连接的接口
	    * @param con
	    */
	   public void builderRelease(Connection con){
		   try {
			release(con);
		} catch (SQLException | InterruptedException e) {
			e.printStackTrace();
		}
	   }
	   
	   /**
	    * 得到缓存池中的数量，这里提供一个供外部访问的接口
	    * @return
	    */
	   public int builderCacheCount(){
		   return getCacheCount();
	   }
	   
	   /**
	    * 得到新建连接的记录池中连接的数量，这里提供一个供外部访问的接口
	    */
	   public int builderNewCacheCount(){
		   return getNewCacheCount();
	   }
	   
	   /**
	    * 关闭数据库连接池，这里提供一个供外部访问的接口
	    */
	   public void builderCloseConnectionPool(){
		   try {
			closeConnectionPool();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	   }
	   
	   /**
	     * 循环检查机制，检查连接池是否被回收
	     * 并且检查缓存池中的连接是否可用
	     * 可以再配置类Deploy中进行配置
	     * 建议启动
	     */
	    private void openTimerCheck(){
	    		scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
	    		scheduledExecutorService.scheduleAtFixedRate(this, MS_TIME, MS_TIME, TimeUnit.SECONDS);

	    }

	    /**
	     * Runnable的实现方法
	     */
	    @Override
	    public void run() {
	    	/*如果内存被回收，进行重新启动*/
	    	if (URL == null || USERNAME == null || PASSWORD == null || DRIVER_CLASS_NAME == null) {
	    		return;	
	    	} 
	    	else{
	    		try {
	    			/*检查缓存池和新建记录池，清楚不可用的连接*/
	    			checkDisconnect();
	    			/*检查查询连接是否可用，如果不可用清空之后重新加载*/
	    			loadSelectConnctionHandler();
	    			/*检查缓存池是否已满，如果未满，并且可新建连接的数量大于或等于缓存池中空的数量，可补充缓冲池连接*/
	    			padCachePool();
	    		} catch (SQLException | ClassNotFoundException | InterruptedException e) {
	    			// TODO Auto-generated catch block
	    			e.printStackTrace();
	    		} 
	    	}
	    }
    }
}  
